﻿using System;
using System.Linq;
using System.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Olitee.WorkScheduler;
using Olitee.WorkScheduler.Tests.Model;

namespace Olitee.WorkScheduler.Tests.SchedulerTests
{
    [TestClass]
    public class WhenProcessingWork
    {
        [TestMethod]
        public void WorkCanBeProcessedImmediately()
        {
            Scheduler s = new Scheduler();

            s.RegisterWorkProcessor<TestWorkItem, TestWorkProcessor>();

            bool expected = true;
            bool actual = s.StartWorkItemImmediately(new TestWorkItem()).Result.Status == Enumerations.WorkItemStatus.Complete;

            Assert.AreEqual(expected, actual);
        }

        [TestMethod]
        [ExpectedException(typeof(Exception))]
        public void MissingGenericWorkProcessorsThrowException()
        {
            Scheduler s = new Scheduler();

            // Note - no work processors are registered for this test!

            // The following line should throw an exception:
            s.StartWorkItemImmediately(new TestWorkItem()).Wait();

            Assert.Fail("The scheulder failed to throw an execption for a missing work processor.");
        }

        [TestMethod]
        public void AddingAWorkItemSucceeds()
        {
            var s = GetTestScheduler();

            s.AddWorkItem(new TestWorkItem());
            Assert.IsTrue(s.AllWorkItems.Count() > 0);
        }

        [TestMethod]
        public void AddingAWorkItemReturnsAFunctionalTask()
        {
            var s = GetTestScheduler();

            var itemIn = new TestWorkItem();
            itemIn.SimulatedProcessingDelay = 3000;

            s.StartScheduler();

            // Add a work item, and wait for the result.
            var itemOut = s.AddWorkItem(itemIn).Result;
             
            Assert.AreEqual(itemIn.Id, itemOut.Id);

        }

        [TestMethod]
        public void TheWorkConcurrencyIsLimitedByTheScheduler()
        {
            var s = GetTestScheduler();

            // Add a load of work items
            for (int i = 0; i < 10; i++)
            {
                s.AddWorkItem(new TestWorkItem { SimulatedProcessingDelay = 300 });
            }

            // Increase the polling interval for testing
            s.SchedulerPollInterval = TimeSpan.FromMilliseconds(50);

            // Limit the concurrency
            s.MaximumConcurrentWorkProcesses = 4;

            s.StartScheduler();

            // add one extra item
            var lastWorkItem = new TestWorkItem();
            s.AddWorkItem(lastWorkItem);

            // Wait 500ms
            Thread.Sleep(500);

            // Check the item is still unprocessed
            Assert.IsTrue(lastWorkItem.Status == Enumerations.WorkItemStatus.Scheduled);

            // Wait a further 5 seconds
            Thread.Sleep(5000);

            // Check the item is now processed
            Assert.IsFalse(lastWorkItem.Status == Enumerations.WorkItemStatus.Scheduled);

        }

        private Scheduler GetTestScheduler()
        {
            var s = new Scheduler();
            s.RegisterWorkProcessor<TestWorkItem, TestWorkProcessor>();
            return s;
        }

    }
}
